<?php


//date_default_timezone_set("Asia/Shanghai");
class Domain_Sandpay
{
	
	//--------------------------------------------1、基础参数配置------------------------------------------------
	
	// const API_HOST = 'https://cashapi.sandpay.com.cn/gateway'; // 生产
	const API_HOST = 'https://scfp-uat.sand.com.cn/gateway'; // uat
	
	const API_MID = '68888TS125511'; // 商户号
	
	const SD_PUB_KEY = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvP/Zvu+8R6imiBQi5zhxNFJ4WTQbKfjsfaz7WeNnZDgFJm+jSo470U1Tm3luEHFqgUmh1D+0KYR3BnhUuhKa/3kGYshq1*******'; // 杉德公钥
	
	const PRI_KEY = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxy6VyeRw1oSLqfP8guasaLqqzNyjkdRRph4yS3qZWrW6SL2zHN9dVdzumpWQZokCTKJ7148FgrseiBMh7VJlnPHbwDAP2Q97qe5RG58bOKkyYz866cFgc3sKJXeEwrQ2as9ru7zlEroQ*********'; // 商户私钥
	
	//--------------------------------------------end基础参数配置------------------------------------------------
	
	
	/**
	 * 私钥签名
	 * @param $plainText
	 * @param $path
	 * @return string
	 * @throws Exception
	 */
	function sign($plainText, $path)
	{
		//$plainText = json_encode($plainText);
		try{
			$resource = openssl_pkey_get_private($path);
			$result   = openssl_sign($plainText, $sign, $resource, OPENSSL_ALGO_SHA256);
			//在PHP8.0以后，即时编译（JIT）模式, 性能已经提高了90%, 你不再需要手动释放openssl_free_key()资源。
			//PHP的垃圾回收机制会自动处理这些内存管理问题。所以，你不需要担心内存占用的问题。
			openssl_free_key($resource);
			if(!$result){
				throw new \Exception('签名出错'.$plainText);
			}
			
			return base64_encode($sign);
		} catch(\Exception $e){
			throw $e;
		}
	}
	
	public function getSignContent($params)
	{
		unset($params['sign']);
		ksort($params);
		
		$stringToBeSigned = "";
		$i                = 0;
		foreach($params as $k => $v){
			if(is_array($v)){
				ksort($v);
				$v = json_encode($v);
			}
			if(false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)){
				if($i == 0){
					$stringToBeSigned .= "$k"."="."$v";
				} else{
					$stringToBeSigned .= "&"."$k"."="."$v";
				}
				$i++;
			}
		}
		
		unset ($k, $v);
		
		return $stringToBeSigned;
	}
	
	// 获取api的url
	public function getApiUrl()
	{
		if(strpos($_SERVER['SERVER_NAME'], 'zhiboxiu.com.cn') === false){
			// 测试环境
			$url = 'https://scfp-uat.sand.com.cn/gateway/trade';
		} else{
			// 生产环境
			$url = 'https://cashapi.sandpay.com.cn/gateway/trade';
		}
		
		return $url;
	}
	
	// 获取公钥
	public function getSandPublicKey()
	{
		$ds   = DIRECTORY_SEPARATOR;
		$data = file_get_contents(__DIR__.$ds.'sand'.$ds.'sand_pro.cer');
		
		/*// 把 .cer 文件内容转换为 X.509 格式
		$cert = openssl_x509_read($data);
		// 提取证书的主题信息
		$info = openssl_x509_parse($cert);
		// 释放证书资源
		openssl_x509_free($cert);*/
		
		$pubKey = '-----BEGIN CERTIFICATE-----'.PHP_EOL
				  .chunk_split(base64_encode($data), 64, PHP_EOL)
				  .'-----END CERTIFICATE-----'.PHP_EOL;
		
		return $pubKey;
	}
	
	// 获取商户私钥
	public function getMerchPrivateKey()
	{
		$ds    = DIRECTORY_SEPARATOR;
		$data  = file_get_contents(__DIR__.$ds.'sand'.$ds.'sanduatprikey.pfx');
		$pass  = '123456';
		$certs = array();
		$ok    = openssl_pkcs12_read($data, $certs, $pass);
		
		return $certs;
	}
	
	/**
	 * 公钥验签
	 * @param $plainText
	 * @param $sign
	 * @param $path
	 */
	public function verify($plainText, $sign, $path)
	{
		$resource = openssl_pkey_get_public($path);
		$result   = openssl_verify($plainText, base64_decode($sign), $resource, 'SHA256');
		//在PHP8.0以后，即时编译（JIT）模式, 性能已经提高了90%, 你不再需要手动释放openssl_free_key()资源。
		//PHP的垃圾回收机制会自动处理这些内存管理问题。所以，你不需要担心内存占用的问题。
		openssl_free_key($resource);
		
		if(!$result){
			return array('code' => '02002', 'msg' => '签名验证未通过,plainText:'.$plainText.'。sign:'.$sign, 'info' => array());
		}
		
		return $result;
	}
	
	
	/**
	 * 发送请求
	 * @param $url
	 * @param $param
	 * @return bool|mixed
	 * @throws Exception
	 */
	function http_post_json($url, $param)
	{
		if(empty($url) || empty($param)){
			return false;
		}
		//NONE模式
		if($param['encryptType'] == 'NONE'){
			$param['bizData'] = json_decode($param['bizData']);  //字符串转对象
		}
		$param = json_encode($param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
		try{
			
			$ch = curl_init();//初始化curl
			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			//正式环境时解开注释
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
			$data = curl_exec($ch);//运行curl
			curl_close($ch);
			
			if(!$data){
				throw new \Exception('请求出错');
			}
			
			return $data;
		} catch(\Exception $e){
			throw $e;
		}
	}
	
	// 数组解析成字符串
	function parse_result($result)
	{
		$arr      = array();
		$response = urldecode($result);
		$arrStr   = explode('&', $response);
		foreach($arrStr as $str){
			$p         = strpos($str, "=");
			$key       = substr($str, 0, $p);
			$value     = substr($str, $p + 1);
			$arr[$key] = $value;
		}
		
		return $arr;
	}
	
	
	// 使用平台公钥加密
	public function Encrypt($request, $publicKey)
	{
		if($request['encryptType'] == 'AES'){
			$key       = $this->getRandString(16);
			$json_data = json_encode($request['bizData']);
			// 加密报文体
			$encryptdata = $this->AESEncrypt($json_data, $key);
			// // 使用商户公钥加密后的，加密key
			$pubKey   = "-----BEGIN PUBLIC KEY-----\n".
						wordwrap($publicKey, 64, "\n", true).
						"\n-----END PUBLIC KEY-----";
			$resource = openssl_pkey_get_public($pubKey);
			$key      = $this->RSAEncryptByPub($key, $resource);
			// 整理
			$request['encryptKey'] = $key;
			$request['bizData']    = $encryptdata;
		}
		
		return $request;
	}
	
	
	// 使用商户私钥解密
	public function Decrypt($request, $priKey)
	{
		if($request['encryptType'] == 'AES'){
			$priKey      = "-----BEGIN RSA PRIVATE KEY-----\n".
						   wordwrap($priKey, 64, "\n", true).
						   "\n-----END RSA PRIVATE KEY-----";
			$resource    = openssl_pkey_get_private($priKey);
			$key         = $this->RSADecryptByPri($request['encryptKey'], $resource);
			$encryptdata = $this->AESDecrypt($request['bizData'], $key);
			$encryptdata = json_decode($encryptdata, 320);
		} else{
			$encryptdata = "";
		}
		
		return $encryptdata;
	}
	
	
	// 检查是否为空
	protected function checkEmpty($value)
	{
		if(!isset($value))
			return true;
		if($value === null)
			return true;
		if(trim($value) === "")
			return true;
		
		return false;
	}
	
	public function RSAEncryptByPub($plainText, $puk)
	{
		if(!openssl_public_encrypt($plainText, $cipherText, $puk, OPENSSL_PKCS1_PADDING)){
			throw new \Exception('AESKey 加密错误');
		}
		
		return base64_encode($cipherText);
	}
	
	/**
	 * 私钥解密AESKey
	 * @param $cipherText
	 * @param $prk
	 */
	public function RSADecryptByPri($cipherText, $prk)
	{
		if(!openssl_private_decrypt(base64_decode($cipherText), $plainText, $prk, OPENSSL_PKCS1_PADDING)){
			return array('code' => '02002', 'msg' => 'AESKey 解密错误', 'info' => array());
		}
		
		return (string)$plainText;
	}
	
	
	/**
	 * AES加密
	 * @param $plainText
	 * @param $key
	 * @return string
	 * @throws \Exception
	 */
	public function AESEncrypt($plainText, $key)
	{
		ksort($plainText);
		$plainText = json_encode($plainText, JSON_UNESCAPED_UNICODE);
		//var_dump($plainText);
		$ivlen = openssl_cipher_iv_length($cipher = "AES-128-ECB");
		$iv    = !$ivlen ? "" : openssl_random_pseudo_bytes($ivlen);
		//$iv = openssl_random_pseudo_bytes($ivlen);
		$result = openssl_encrypt($plainText, 'AES-128-ECB', $key, OPENSSL_RAW_DATA, $iv);
		//var_dump($iv);
		if(!$result){
			throw new \Exception('报文加密错误');
		}
		
		return base64_encode($result);
	}
	
	/**
	 * AES解密
	 * @param $cipherText
	 * @param $key
	 * @return string
	 * @throws \Exception
	 */
	public function AESDecrypt($cipherText, $key)
	{
		$result = openssl_decrypt(base64_decode($cipherText), 'AES-128-ECB', $key, 1);
		
		if(!$result){
			throw new \Exception('报文解密错误', 2003);
		}
		
		return $result;
	}
	
	/**
	 * 获取私钥
	 * @param $path
	 * @param $pwd
	 * @return mixed
	 * @throws Exception
	 */
	public function loadPk12Cert($path, $pwd)
	{
		try{
			$file = file_get_contents($path);
			if(!$file){
				throw new \Exception('loadPk12Cert::file_get_contents');
			}
			
			if(!openssl_pkcs12_read($file, $cert, $pwd)){
				throw new \Exception('loadPk12Cert::openssl_pkcs12_read ERROR');
			}
			
			return $cert['pkey'];
		} catch(\Exception $e){
			throw $e;
		}
	}
	
	/**
	 * 获取公钥
	 * @param $path
	 * @return mixed
	 * @throws Exception
	 */
	public function loadX509Cert($path)
	{
		try{
			$file = file_get_contents($path);
			if(!$file){
				throw new \Exception('loadx509Cert::file_get_contents ERROR');
			}
			
			$cert   = chunk_split(base64_encode($file), 64, "\n");
			$cert   = "-----BEGIN CERTIFICATE-----\n".$cert."-----END CERTIFICATE-----\n";
			$res    = openssl_pkey_get_public($cert);
			$detail = openssl_pkey_get_details($res);
			openssl_free_key($res);
			if(!$detail){
				throw new \Exception('loadX509Cert::openssl_pkey_get_details ERROR');
			}
			
			return $detail['key'];
		} catch(\Exception $e){
			throw $e;
		}
	}
	
	/**
	 * 生成AESKey  随机16位字符串
	 * @param $size
	 * @return string
	 */
	public function getRandString($size)
	{
		$str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
		$arr = array();
		for($i = 0; $i < $size; $i++){
			$arr[] = $str[mt_rand(0, 61)];
		}
		
		return implode('', $arr);
	}
	
}


